package pl.edu.pw.perg.compinfodb.datamodel;

import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.swing.JComboBox;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableModel;

import org.jdesktop.swingx.JXTable;
import org.jdesktop.swingx.decorator.ColorHighlighter;
import org.jdesktop.swingx.decorator.Highlighter;

import pl.edu.pw.perg.compinfodb.command.IActiveModelListener;
import pl.edu.pw.perg.compinfodb.command.IModelModificationListener;
import pl.edu.pw.perg.compinfodb.command.IMutableTableModel;
import pl.edu.pw.perg.compinfodb.entities.ElementType;
import pl.edu.pw.perg.compinfodb.entities.Field;
import pl.edu.pw.perg.compinfodb.entities.PhysicalElement;
import pl.edu.pw.perg.compinfodb.entities.VirtualElement;
import pl.edu.pw.perg.compinfodb.ui.ColorRenderer;
import pl.edu.pw.perg.compinfodb.ui.JXSelectionModel;
import pl.edu.pw.perg.compinfodb.ui.RowHighlightPredicate;

public class ElementTableModel implements TableModel, ListSelectionListener,
		IStructureTreeSelectionListener, IElementTypeSelectionListener,
		ISearchFilterListener, IRefreshListener, IMutableTableModel,
		FocusListener {

	private static final String ALL = "ALL";
	private static final String SPARE = "SPARE";
	private ElementDataList datalist;
	private String[] columnNames;
	private final String[] constColumnsNames = { "LABEL", "ELEMENT_TYPE",
			"LOCATION", "SLOT" };
	private List<TableModelListener> listeners = new LinkedList<TableModelListener>();
	private List<IPhysicalElementSelectionListener> peListeners = new ArrayList<IPhysicalElementSelectionListener>();
	private List<IConditionChangeListener> markedRowSelectionListeners = new LinkedList<IConditionChangeListener>();
	private List<IConditionChangeListener> noneRowMarkedListeners = new LinkedList<IConditionChangeListener>();
	private List<SearchTableModel> searchTableModels = new ArrayList<SearchTableModel>();
	private int markedRowId = -1;
	private RowHighlightPredicate predicate = new RowHighlightPredicate(-1);
	private ColorHighlighter highlighter = new ColorHighlighter(predicate,
			Color.orange, Color.black);
	private VirtualElement virtualElt = null;
	private PhysicalElement physcialElt = null;
	private ElementType type;
	private boolean editable;
	private List<IModelModificationListener> modelModificationListeners = new LinkedList<IModelModificationListener>();
	private List<IActiveModelListener> activeModelListeners = new LinkedList<IActiveModelListener>();

	private FlexibleTableColumnModel tcm = new FlexibleTableColumnModel(
			new ColorRenderer());
	private boolean modified;
	private boolean spareMode = false;

	public ElementTableModel(ElementDataList list) {
		datalist = list;
		columnNames = constColumnsNames;
		// highlighter.setBackground(Color.orange);
		// recreateColumnModel();
	}

	@SuppressWarnings("unchecked")
	public Class getColumnClass(int column) {
		return String.class;
	}

	public int getColumnCount() {
		return columnNames.length;
	}

	public String getColumnName(int columnIndex) {
		return columnNames[columnIndex];
	}

	public Object getValueAt(int row, int column) {
		if (datalist.get(row) == null)
			return null;
		return datalist.get(row).getValue(columnNames[column]);
	}

	public void setElementType(ElementType type) {
		if (type == this.type && type != null)
			datalist.setTypeChanged(false);
		else {
			datalist.setTypeChanged(true);
			this.type = type;
			datalist.setTypeFilter(type);

			Iterator<Field> it = null;
			List<String> columns = new ArrayList<String>();

			if (type != null) {
				for (it = type.getFields().iterator(); it.hasNext();) {
					String string = (String) it.next().getFieldName();
					columns.add(string);
				}
			}
			columns.addAll(Arrays.asList(constColumnsNames));

			columnNames = columns.toArray(new String[columns.size()]);
			recreateColumnModel();
		}
		datalist.search();

	}

	public void recreateColumnModel() {
		for (TableModelListener l : listeners) {
			JXTable t = ((JXTable) l);
			tcm.setColumnNames(columnNames);
			t.setColumnModel(tcm);
			t.createDefaultColumnsFromModel();
			t.packAll();
			columnModelChanged();

		}
	}

	public void search(Map<String, String> filter) {
		if (virtualElt.getPhysicalElement() != null)
			datalist.setDataId(virtualElt.getId());
		else
			datalist.setDataId(null);
		datalist.search(filter);
		predicate.setHighlitedRow(datalist.getMarkedRowId());
		modelModified();
	}

	public void addTableModelListener(TableModelListener l) {
		listeners.add(l);

	}

	public void addModelModificationListener(IModelModificationListener l) {
		modelModificationListeners.add(l);

	}

	public void addActiveModelListener(IActiveModelListener l) {
		activeModelListeners.add(l);

	}

	public void addMarkedRowSelectionListener(IConditionChangeListener l) {
		markedRowSelectionListeners.add(l);

	}

	public void addNoneRowMarkedListener(IConditionChangeListener l) {
		noneRowMarkedListeners.add(l);

	}

	public void addPhysicalElementSelectionListener(
			IPhysicalElementSelectionListener pes) {
		peListeners.add(pes);
	}

	public int getRowCount() {
		return datalist.size();
	}

	public boolean isCellEditable(int rowIndex, int columnIndex) {
		if (!editable)
			return false;
		return checkColumnEditability(columnIndex);
	}

	public void removeTableModelListener(TableModelListener l) {
		listeners.remove(l);

	}

	public void setValueAt(Object value, int rowIndex, int columnIndex) {
		if (value.equals(""))
			return;
		// datalist.get(rowIndex).setValue(getColumnName(columnIndex), value);
		datalist.setValueAt(rowIndex, getColumnName(columnIndex), value);
		modelModified();

	}

	public void actionPerformed(ActionEvent e) {
		JComboBox cb = (JComboBox) e.getSource();
		setElementType((ElementType) cb.getModel().getSelectedItem());

	}

	public void selectionChanged(VirtualElement ve) {
		virtualElt = ve;
		if (virtualElt != null) {
			if (virtualElt.getName().equals(SPARE))
				setSpare(true);
			else
				setSpare(false);
			if (virtualElt.getName().equals(ALL))
				setAll(true);
			else
				setAll(false);
			if (!virtualElt.getName().equals(ALL) && !virtualElt.getName().equals(SPARE))
				datalist.setDataId(virtualElt.getId());
		}
		else 
			datalist.setDataId(null);

		setElementType(virtualElt.getElementType());
		reloadTable();
		predicate.setHighlitedRow(datalist.getMarkedRowId());
		modelModified();
	}

	private void setAll(boolean isAll) {
		datalist.setAll(isAll);
		
	}

	private void setSpare(boolean isSpare) {
		datalist.setSpare(isSpare);
	}

	public void valueChanged(ListSelectionEvent e) {
		int row = ((JXSelectionModel) e.getSource()).getModelSelectionIndex();
		TableData td = datalist.get(row);
		if (td == null)
			return;
		PhysicalElement ce = td.getPe();
		for (IPhysicalElementSelectionListener pe : peListeners) {

			pe.selectionChanged(ce);
		}
		// notyfikuj zainteresowanych czy jest wybrany zaznaczony wiersz
		for (IConditionChangeListener l : markedRowSelectionListeners)
			l.conditionChanged(row == datalist.getMarkedRowId());

		for (IConditionChangeListener l : noneRowMarkedListeners)
			l.conditionChanged(-1 == datalist.getMarkedRowId()
					&& ce.getVirtualElement() == null && !isSpare());

	}

	private boolean isSpare() {
		return datalist.isSpare();
	}

	public void reloadTable() {
		for (TableModelListener table : listeners) {
			table.tableChanged(new TableModelEvent(this));
		}
	}

	public void selectionChanged(ElementType selectedElement) {
		setElementType(selectedElement);
		for (IPhysicalElementSelectionListener pe : peListeners) {
			pe.selectionChanged(null);
		}
		reloadTable();
	}

	// public void switchToSearchMode(boolean searchMode) {
	// System.out.println("called");
	//		
	// }

	public void addColumnModelListener(SearchTableModel stm) {
		searchTableModels.add(stm);
	}

	public void columnModelChanged() {
		for (SearchTableModel stm : searchTableModels) {
			stm.setColumnModel(tcm);
		}
	}

	// public void setSearchFilter(Map<String, String> filter) {
	//
	// }

	public void filterChanged(Map<String, String> filter) {
		search(filter);
		reloadTable();
	}

	public int getMarkedRowId() {
		return markedRowId;
	}

	public RowHighlightPredicate getPredicate() {
		return predicate;
	}

	public Highlighter getHighlighter() {
		return highlighter;
	}

	public void refresh() {
		search(null);

	}

	public void setEditable(boolean editable) {
		this.editable = editable;
	}

	public boolean isModified() {
		return datalist.isModified();
	}

	public void save() {
		datalist.save();
		modelModified();

	}

	private void modelModified() {
		for (IModelModificationListener l : modelModificationListeners) {
			l.modelModified(isModified());
		}
	}

	public void focusGained(FocusEvent e) {
		setActive();
	}

	public void focusLost(FocusEvent e) {
		// TODO Auto-generated method stub

	}

	public void addNew() {
		datalist.addNew();
		modelModified();
		reloadTable();
	}

	public void setActive() {
		for (IActiveModelListener l : activeModelListeners) {
			l.setActiveModel(this);
		}

	}

	private boolean checkColumnEditability(int colIndex) {
		boolean flag = true;
		String name = columnNames[colIndex];
		for (int i = 0; i < constColumnsNames.length; i++) {
			if (name.equals(constColumnsNames[i])) {
				flag = false;
				break;
			}
		}
		if (name.equals("LABEL"))
			flag = true;
		return flag;
	}

	public boolean isEditable() {
		return editable;
	}
}
